Offline-First Architecture
Offline-first means the application works fully without a network, and treats connectivity as an opportunity to synchronise rather than a precondition for function. It is not "offline mode" bolted onto an online application; the data model, the identity strategy and the workflow all change.
It applies far beyond community health: rural facilities with daily outages, mobile outreach and campaigns, disaster response, and any national system whose edge is less reliable than its centre.
The core inversion
| Online-first | Offline-first |
|---|---|
| The server holds the record; the client is a view | The device holds a complete record of record for its scope |
| Writes go to the server | Writes go to local storage, then replicate |
| Identifiers assigned by the server | Identifiers assigned locally, reconciled later |
| Validation server-side | Validation local, re-validated on sync |
| Conflicts prevented by locking | Conflicts are normal and must be resolved |
| Failure is an error state | Disconnection is the expected state |
The consequence that surprises teams: the device must be able to create identity. A CHW registering a newborn in a village cannot wait for the client registry. Local identifier generation with later reconciliation is unavoidable.
Sync architecture
Device Server
────── ──────
┌─────────────────┐ ┌──────────────────┐
│ Local store │ │ Central store │
│ (SQLite, │ │ │
│ encrypted) │ │ │
└────────┬────────┘ └────────┬─────────┘
│ │
┌────────▼────────┐ push local changes ┌────▼─────────┐
│ Outbound queue │ ─────────────────────▶ │ Sync │
│ (ordered, │ │ endpoint │
│ idempotent) │ ◀───────────────────── │ │
└─────────────────┘ pull changes since └──────────────┘
last sync token
Design elements:
- A sync token or watermark per device: what has been sent, what has been received. Not a timestamp alone — clock skew on cheap devices is real.
- Idempotent writes. Every change carries a client-generated identifier so a retried sync does not duplicate it.
- Scoped pull. A device receives only its catchment's data. Pulling a national dataset onto a phone is neither feasible nor acceptable.
- Chunked, resumable transfer. A sync interrupted at 80% must resume, not restart. Connectivity windows are short.
- Bandwidth discipline. Compress; send deltas rather than whole records; avoid images unless necessary and defer them to a separate lower-priority queue.
- Sync status visible to the user. The worker must know whether their data has reached the server — and when it last did. Silent failure is the worst outcome.
Conflict resolution
Two people edited the same record while disconnected. This will happen; the question is only what the system does.
| Strategy | How | Suits |
|---|---|---|
| Last write wins | Latest timestamp survives | Simple attributes where the newest is genuinely best — a phone number |
| Field-level merge | Merge per field rather than per record | Demographic records edited by different people for different reasons |
| Append-only / event log | Never update; append observations | Clinical data — the correct default |
| Human resolution | Queue for a person to decide | Identity merges, conflicting clinical assertions |
| Source priority | A defined authority wins | Registry data synced to the device |
Prefer append-only for clinical data. A blood pressure recorded by a CHW and another recorded at a facility are not a conflict — they are two observations, both true, at different times. Modelling clinical data as immutable events with timestamps eliminates most apparent conflicts entirely, and preserves the clinical record's integrity.
Genuine conflicts concentrate in mutable state: demographics, enrolment status, household composition, and identity. Those are the cases that need explicit strategies and, sometimes, human adjudication.
Never resolve a conflict by discarding data silently. Retain both versions, record the resolution, and make it auditable.
Identity offline
The hardest problem in this architecture.
Device creates a record
│ local UUID assigned immediately
▼
Work proceeds — visits, observations, referrals all reference the local ID
│
▼ on sync
Server attempts resolution against the client registry
│
├── confident match ──▶ link local ID to shared ID; device updated
├── no match ──▶ create new registry entry; shared ID returned
└── uncertain ──▶ review queue; local ID remains usable meanwhile
Requirements:
- Local identifiers are UUIDs, not sequential numbers — sequential identifiers collide across devices.
- The device stores both local and shared identifiers once resolved, and continues to accept the local one.
- Work is never blocked on resolution. A record in the review queue is still a record a CHW can add visits to.
- Merges propagate to devices. When the registry merges two identities, the devices holding either must learn about it.
- The worker's knowledge is captured. A CHW confirming "this is the same woman I registered last year" is high-quality matching evidence — record it.
See client registry.
Reference data on the device
Devices need local copies of what would otherwise be a server lookup:
- Facility registry subset — for referral destinations
- Value sets and code lists from the terminology service
- Decision support rules and schedules from the DAK
- Forms and application configuration
Each needs: a version, a mechanism to update when connectivity permits, a record of which version was in force when data was captured, and a size budget. A 1 GB terminology download to a phone on a metered connection is not a plan.
Security on the device
Devices are lost, stolen, shared and sold. They hold clinical data.
- Encrypt local storage. Full-device encryption plus application-level encryption of the clinical store.
- Authenticate the user locally — a PIN or biometric that works offline, since the device cannot reach an identity provider. Offline authentication necessarily uses cached credentials; bound the cache lifetime.
- Limit local data scope and retention. Hold the catchment, not the district; purge records the worker no longer needs.
- Remote wipe, effective at next contact, with a documented procedure.
- Screen privacy. Work happens in households, in front of family members.
- Device lifecycle: enrolment, replacement, decommissioning with data destruction. Plan for a 20–30% annual device turnover.
Store-and-forward at the facility
The same pattern applies to facility systems, at a different scale. A facility EMR should:
- Hold its own record locally and function fully during a central outage
- Cache registry and terminology data with a defined refresh interval and a stated maximum staleness
- Queue outbound exchange messages with retry and backoff
- Surface the queue's depth and age to local staff
- Have a documented degraded-mode procedure, rehearsed
This is what makes a centralised architecture survivable in practice.
Testing
Offline behaviour is only real if it is tested under realistic conditions:
- Full disconnection for days, with substantial work performed
- Intermittent and very slow connections, not just on/off
- Sync interrupted mid-transfer, repeatedly
- Two devices editing the same record while disconnected
- Device clock wrong by hours or days
- Storage full
- App upgraded while unsynced data is pending — the case most commonly missed, and the one that loses data
- Sync after a long absence, where a great deal has changed on both sides
Technologies
| Layer | Options |
|---|---|
| Local store | SQLite (with SQLCipher), Realm, IndexedDB, PouchDB |
| Sync protocol | CouchDB replication (used by CHT), custom REST sync, CRDT libraries |
| Mobile platforms | Android (dominant in this space), progressive web apps |
| Frameworks | Community Health Toolkit, CommCare, DHIS2 Android SDK, ODK-X |
CouchDB-style replication is well suited to this problem and is the reason several health platforms adopted it. CRDTs are attractive in theory and rarely map cleanly onto clinical semantics — an automatically merged medication list is not obviously safe.
References
- Community Health Toolkit — https://communityhealthtoolkit.org/
- DHIS2 Android SDK — https://dhis2.org/android/
- CouchDB replication protocol — https://docs.couchdb.org/en/stable/replication/protocol.html
- SQLCipher — https://www.zetetic.net/sqlcipher/
- Community health, registries